﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using GeneticAlgorythm.Algorythm;
using GeneticAlgorythm.Problem;
using GeneticAlgorythm.Utils;
using System.Collections.Generic;
using System.Diagnostics;

namespace UnitTestInzynierka
{
    [TestClass]
    public class AlgorytmTests5x5
    {
        private ObjectiveFunctionCalculatorCPU _calculatorCPU = new ObjectiveFunctionCalculatorCPU();
        private ObjectiveFunctionCalculatorGPU _calculatorGPU = new ObjectiveFunctionCalculatorGPU();
        private ObjectiveFunctionCalculatorMyGPU _calculatorMyGPU = new ObjectiveFunctionCalculatorMyGPU();
        private List<Chromosome> _chromosomes = new List<Chromosome>();
        private int _numberOfRepetitions = 20;
        private int _probelmSize = 30;
        private Stopwatch _timer = new Stopwatch();

        Individual testAlgorythm(string populationFile, 
                             int numberOfIteratons,
                             int percentageOfIndividualsInNewPopulation,
                             int percentageOfMutations,
                             bool allCrossoverTypes,
                             Crossover crossoverType,
                             int individualsLifeLength = 0)
        {
            ProblemInstance problem = Utils.Utils.getKra30b(_calculatorMyGPU);
            PopulationFileSaver fileSaver = new PopulationFileSaver(populationFile);

            Population population = new Population(fileSaver, problem, individualsLifeLength);
            population.setPercentageOfIndividualsInNewPopulation(percentageOfIndividualsInNewPopulation);
            Selection selection = new TournamentSelection();
            Crossover crossover = crossoverType;

            for (int i = 0; i < numberOfIteratons; i++)
            {
                if (allCrossoverTypes)
                {
                    crossover = RandomGenerator.getInstance().generateCrossoverType();
                }
                population.nexIteration(selection, crossover, percentageOfMutations);
            }
            return population.getBestIndividual();
        }

        Tuple<int, long> testAlgorythmTillBestValueReached(string populationFile,
                     int numberOfIteratons,
                     int percentageOfIndividualsInNewPopulation,
                     int percentageOfMutations,
                     double bestValue,
                     bool allCrossoverTypes,
                     Crossover crossoverType,
                     ProblemInstance problemInstance,
                     int individualsLifeLength = 0)
        {
            PopulationFileSaver fileSaver = new PopulationFileSaver(populationFile);

            Population population = new Population(fileSaver, problemInstance, individualsLifeLength);
            population.setPercentageOfIndividualsInNewPopulation(percentageOfIndividualsInNewPopulation);
            Selection selection = new TournamentSelection();
            Crossover crossover = crossoverType;

            _timer.Start();
            for (int i = 0; i < numberOfIteratons; i++)
            {
                if (allCrossoverTypes)
                {
                    crossover = RandomGenerator.getInstance().generateCrossoverType();
                }
                population.nexIteration(selection, crossover, percentageOfMutations);
                if(population.getBestIndividual().getObjectiveFunctionValue().Equals(bestValue))
                {
                    _timer.Stop();
                    return new Tuple<int, long>(i, _timer.ElapsedMilliseconds);
                }
            }

            return new Tuple<int, long>(0, 0);
        }

        void testAlgorythNumberOfTimes(string populationFile,
                             int numberOfIteratons,
                             int percentageOfIndividualsInNewPopulation,
                             int percentageOfMutations,
                             bool allCrossoverTypes,
                             Crossover crossoverType,
                             int individualsLifeLength = 0)
        {
            double sumOfValues = 0;
            Individual bestIndividual = new Individual(RandomGenerator.getInstance().generateNewIntegerPermutationChromosome(_probelmSize));
            bestIndividual.setObjectiveFunctionValue(double.MaxValue);
            for (int i = 0; i < _numberOfRepetitions; i++)
            {
                Individual individual = testAlgorythm(populationFile, numberOfIteratons, percentageOfIndividualsInNewPopulation, percentageOfMutations, allCrossoverTypes, crossoverType, individualsLifeLength);
                sumOfValues += individual.getObjectiveFunctionValue();
                if (bestIndividual.getObjectiveFunctionValue() > individual.getObjectiveFunctionValue())
                {
                    bestIndividual = individual;
                }
            }
            System.Console.WriteLine("Wynik: " + sumOfValues / _numberOfRepetitions + ", " + populationFile + ", " + numberOfIteratons + ", " + percentageOfIndividualsInNewPopulation + ", " + percentageOfMutations + ", " + allCrossoverTypes + ", " + individualsLifeLength);
            System.Console.WriteLine(bestIndividual);
            System.Console.WriteLine("Wynik dla najlepszego: " + bestIndividual.getObjectiveFunctionValue());
        }

        void testDifferentMutationValuesWith100Individuals(int percentageOfMutations)
        {
            testAlgorythNumberOfTimes("500Chromosomes_30.txt", 100, 40, percentageOfMutations, true, null);
            testAlgorythNumberOfTimes("500Chromosomes_30.txt", 300, 40, percentageOfMutations, true, null);
            testAlgorythNumberOfTimes("500Chromosomes_30.txt", 1000, 40, percentageOfMutations, true, null);
        }

        void testDifferentMutationValuesWith1000Individuals(int percentageOfMutations)
        {
            testAlgorythNumberOfTimes("1000Chromosomes_20.txt", 1000, 40, percentageOfMutations, true, null);
        }



        void testDifferentSelectionValuesWith100Individuals(int percentageOfIndividualsInNewPopulation)
        {
            testAlgorythNumberOfTimes("500Chromosomes_30.txt", 100, percentageOfIndividualsInNewPopulation, 30, true, null);
            testAlgorythNumberOfTimes("500Chromosomes_30.txt", 300, percentageOfIndividualsInNewPopulation, 30, true, null);
            testAlgorythNumberOfTimes("500Chromosomes_30.txt", 1000, percentageOfIndividualsInNewPopulation, 30, true, null);
        }

        void testDifferentSelectionValuesWith1000Individuals(int percentageOfIndividualsInNewPopulation)
        {
            double sumOfValues = 0;
            Individual bestIndividual = new Individual(RandomGenerator.getInstance().generateNewIntegerPermutationChromosome(_probelmSize));
            bestIndividual.setObjectiveFunctionValue(double.MaxValue);
            for (int i = 0; i < _numberOfRepetitions; i++)
            {
                Individual individual = testAlgorythm("1000Chromosomes_12.txt", 1000, percentageOfIndividualsInNewPopulation, 30, true, null);
                sumOfValues += individual.getObjectiveFunctionValue();
                if (bestIndividual.getObjectiveFunctionValue() > individual.getObjectiveFunctionValue())
                {
                    bestIndividual = individual;
                }
            }
            System.Console.WriteLine("Wynik:" + sumOfValues / _numberOfRepetitions + "1000Chromosomes_12.txt , 1000," + percentageOfIndividualsInNewPopulation + " 30,  true, null");
            System.Console.WriteLine(bestIndividual);
        }

        void testDifferentCrossoverTypesWith100Individuals(bool allCrossoverTypes, Crossover crossoverType)
        {
            testAlgorythNumberOfTimes("100Chromosomes_30.txt", 100, 40, 30, allCrossoverTypes, crossoverType);
            testAlgorythNumberOfTimes("200Chromosomes_30.txt", 100, 40, 30, allCrossoverTypes, crossoverType);
            testAlgorythNumberOfTimes("500Chromosomes_30.txt", 100, 40, 30, allCrossoverTypes, crossoverType);
            testAlgorythNumberOfTimes("100Chromosomes_30.txt", 300, 40, 30, allCrossoverTypes, crossoverType);
            testAlgorythNumberOfTimes("200Chromosomes_30.txt", 300, 40, 30, allCrossoverTypes, crossoverType);
            testAlgorythNumberOfTimes("500Chromosomes_30.txt", 300, 40, 30, allCrossoverTypes, crossoverType);
            testAlgorythNumberOfTimes("100Chromosomes_30.txt", 1000, 40, 30, allCrossoverTypes, crossoverType);
            testAlgorythNumberOfTimes("200Chromosomes_30.txt", 1000, 40, 30, allCrossoverTypes, crossoverType);
            testAlgorythNumberOfTimes("500Chromosomes_30.txt", 1000, 40, 30, allCrossoverTypes, crossoverType);
            //testAlgorythNumberOfTimes("1000Chromosomes_30.txt", 100, 40, 30, allCrossoverTypes, crossoverType);
        }

        void testDifferentCrossoverTypesWith1000Individuals(bool allCrossoverTypes, Crossover crossoverType)
        {
            double sumOfValues = 0;
            Individual bestIndividual = new Individual(RandomGenerator.getInstance().generateNewIntegerPermutationChromosome(_probelmSize));
            bestIndividual.setObjectiveFunctionValue(double.MaxValue);
            for (int i = 0; i < _numberOfRepetitions; i++)
            {
                Individual individual = testAlgorythm("1000Chromosomes_12.txt", 1000, 40, 30, allCrossoverTypes, crossoverType);
                sumOfValues += individual.getObjectiveFunctionValue();
                if (bestIndividual.getObjectiveFunctionValue() > individual.getObjectiveFunctionValue())
                {
                    bestIndividual = individual;
                }
            }
            System.Console.WriteLine("Wynik:" + sumOfValues / _numberOfRepetitions + "1000Chromosomes_12.txt , 1000, 40, 30," + allCrossoverTypes + (crossoverType == null ? "null" : crossoverType.GetType().ToString()));
            System.Console.WriteLine(bestIndividual);
        }

        void testDifferentLifeLengthWith100Individuals(int individualsLifeLength)
        {
            testAlgorythNumberOfTimes("500Chromosomes_30.txt", 100, 40, 30, true, null, individualsLifeLength);
            testAlgorythNumberOfTimes("500Chromosomes_30.txt", 300, 40, 30, true, null, individualsLifeLength);
            testAlgorythNumberOfTimes("500Chromosomes_30.txt", 1000, 40, 30, true, null, individualsLifeLength);
        }

        void testDifferentLifeLengthWith1000Individuals(int individualsLifeLength)
        {
            double sumOfValues = 0;
            Individual bestIndividual = new Individual(RandomGenerator.getInstance().generateNewIntegerPermutationChromosome(_probelmSize));
            bestIndividual.setObjectiveFunctionValue(double.MaxValue);
            for (int i = 0; i < _numberOfRepetitions; i++)
            {
                Individual individual = testAlgorythm("1000Chromosomes_12.txt", 1000, 40, 30, true, null, individualsLifeLength);
                sumOfValues += individual.getObjectiveFunctionValue();
                if (bestIndividual.getObjectiveFunctionValue() > individual.getObjectiveFunctionValue())
                {
                    bestIndividual = individual;
                }
            }
            System.Console.WriteLine("Wynik:" + sumOfValues / _numberOfRepetitions + "1000Chromosomes_12.txt , 1000, 40, 30, true, null" + individualsLifeLength);
            System.Console.WriteLine(bestIndividual);
        }

        long[] testObjectiveFunctionCalculator(List<Chromosome> chromosomes, ObjectiveFunctionCalculator calculator)
        {
            ProblemInstance problemData = Utils.Utils.getTai256c(calculator);
            float[] result = problemData.calculateObjectiveFunctionValue(chromosomes);
            return calculator.getTimerValues();
        }
        
        long[] testObjectiveFunctionCalculatorCPU()
        {
            long[] sumOfValues = {0, 0};
            for (int i = 0; i < 20; i++)
            {
                long[] returnValues = testObjectiveFunctionCalculator(_chromosomes, _calculatorCPU);
                sumOfValues[0] += returnValues[0];
                sumOfValues[1] += returnValues[1];
            }
            return new long[] { sumOfValues[0] / 20, sumOfValues[1] / 20 };
        }

        long[] testObjectiveFunctionCalculatorGPU()
        {
            long[] sumOfValues = { 0, 0 };
            for (int i = 0; i < 20; i++)
            {
                long[] returnValues = testObjectiveFunctionCalculator(_chromosomes, _calculatorGPU);
                sumOfValues[0] += returnValues[0];
                sumOfValues[1] += returnValues[1];
            }
            return new long[] { sumOfValues[0] / 20, sumOfValues[1] / 20};
        }

        long[] testObjectiveFunctionCalculatorMyGPU()
        {
            long[] sumOfValues = { 0, 0, 0, 0 };
            for (int i = 0; i < 20; i++)
            {
                long[] returnValues = testObjectiveFunctionCalculator(_chromosomes, _calculatorMyGPU);
                sumOfValues[0] += returnValues[0];
                sumOfValues[1] += returnValues[1];
                sumOfValues[2] += returnValues[2];
                sumOfValues[3] += returnValues[3];

            }
            return new long[] { sumOfValues[0] / 20, sumOfValues[1] / 20, sumOfValues[2] / 20, sumOfValues[3] / 20 };
        }

        void addChromosomesAndTestCalculators(int amount)
        {
            for (int i = 0; i < amount;i++)
            {
                _chromosomes.Add(RandomGenerator.getInstance().generateNewIntegerPermutationChromosome(256));
            }
            long[] cpuTimerValues = testObjectiveFunctionCalculatorCPU();
            //long[] gpuTimerValues = testObjectiveFunctionCalculatorGPU();
            long[] myGpuTimerValues = testObjectiveFunctionCalculatorMyGPU();
            System.Console.WriteLine(_chromosomes.Count + " on CPU:" + cpuTimerValues[0] + ", "
                                                                     + cpuTimerValues[1]);
            //System.Console.WriteLine(_chromosomes.Count + " on GPU:" + gpuTimerValues[0] + ", "
             //                                                        + gpuTimerValues[1]);
            System.Console.WriteLine(_chromosomes.Count + " on MyGPU:" + myGpuTimerValues[0] + ", "
                                                                       + myGpuTimerValues[1] + ", "
                                                                       + myGpuTimerValues[2] + ", "
                                                                       + myGpuTimerValues[3]);
        }


        [TestMethod]
        public void Tests()
        {

                //testAlgorythmTillBestValueReached("100Chromosomes_20.txt", 50000, 40, 30, 6922, true, null);
                //testAlgorythmTillBestValueReached("1000Chromosomes_20.txt", 2000, 40, 30, 6922, true, null);
            //testDifferentMutationValuesWith100Individuals(30);
            for (int i = 5; i < 100; i += 5)
            {
                //testDifferentMutationValuesWith100Individuals(i);
            }

            for (int i = 5; i < 100; i += 5)
            {
                //testDifferentMutationValuesWith1000Individuals(i);
            }

            for (int i = 5; i < 100; i += 5)
            {
                //testDifferentSelectionValuesWith100Individuals(i);
            }

            for (int i = 5; i < 100; i += 5)
            {
                //testDifferentSelectionValuesWith1000Individuals(i);
            }

            //testDifferentCrossoverTypesWith100Individuals(true, null);
            //testDifferentCrossoverTypesWith100Individuals(false, new CycleCrossover());
            //testDifferentCrossoverTypesWith100Individuals(false, new PartiallyMatchedCrossover());
            //testDifferentCrossoverTypesWith100Individuals(false, new UniformOrderBasedCrossover());
            //testDifferentCrossoverTypesWith100Individuals(false, new OrderCrossover());

            //testDifferentCrossoverTypesWith1000Individuals(true, null);
            //testDifferentCrossoverTypesWith1000Individuals(false, new CycleCrossover());
            //testDifferentCrossoverTypesWith1000Individuals(false, new PartiallyMatchedCrossover());
            //testDifferentCrossoverTypesWith1000Individuals(false, new UniformOrderBasedCrossover());
            //testDifferentCrossoverTypesWith1000Individuals(false, new OrderCrossover());


            for (int i = 0; i < 16; i++)
            {
                testDifferentLifeLengthWith100Individuals(i);
            }

            for (int i = 0; i < 16; i++)
            {
                //testDifferentLifeLengthWith1000Individuals(i);
            }


            //double sumOfValues = 0;
            //for (int i = 0; i < 10; i++)
            //{
            //    sumOfValues += testAlgorythm("TestPopulation1.txt", 100, 30, 10, false, new CycleCrossover());
            //}
            //System.Console.WriteLine("Wynik:" + sumOfValues / 10 + "TestPopulation1.txt , 100, 30, 10, false, CycleCrossover");


            //sumOfValues = 0;
            //for (int i = 0; i < 10; i++)
            //{
            //    sumOfValues += testAlgorythm("TestPopulation2.txt", 1000, 35, 15, true, null, 10);
            //}
            //System.Console.WriteLine("Wynik:" + sumOfValues / 10 + "TestPopulation2.txt , 1000, 35, 15, true, null, 10");

            //addChromosomesAndTestCalculators(50);
            //for (int i = 1; i < 5; i++ )
            //{
            //    addChromosomesAndTestCalculators(10000);
            //}
               
        }
    }
}
